Better names for taxa
pull_lowlev <- function(taxaVec){
taxaVec <- dplyr::select(Kingdom:Genus)
taxaVec <- na.omit(taxaVec)
taxaVec[length(taxaVec)]
}
theTaxa <- ps %>% tax_table() %>% as("matrix") %>% data.frame() #%>% as.data.frame()
theTaxa[] <- lapply(theTaxa, as.character)
theTaxa$ASVNum <- rownames(theTaxa) %>% parse_number
theTaxa$ASVName <- rownames(theTaxa)
myLowist <- theTaxa %>% pivot_longer(Kingdom:Genus) %>% na.omit %>% group_by(ASVName) %>% summarise(Lowist = last(value))
theTaxa <- theTaxa %>% left_join(myLowist, by = "ASVName")
theTaxa <- theTaxa %>% mutate(JName = str_c(Lowist, ASVNum, sep = "_"))
theTaxa <- theTaxa %>% column_to_rownames("ASVName")
head(theTaxa)
tt2 <- tax_table(theTaxa)
Coercing from data.frame class to character matrix
prior to building taxonomyTable.
This could introduce artifacts.
Check your taxonomyTable, or coerce to matrix manually.
rownames(tt2) <- rownames(theTaxa)
colnames(tt2) <- colnames(theTaxa)
ps_retax <- ps
tax_table(ps_retax) <- tt2
Remove blanks
ps_noblank <- subset_samples(ps_retax, Strain != "Blank")
ps_plusone <- transform_sample_counts(ps_noblank, function(x) x + 1)
Convert to relative abundance
psra <- ps %>% transform_sample_counts( function(x) x/sum(x))
Normalize microibal counts to oyster counts
# ps_oyster <- ps %>% subset_taxa(Order == "Ostreoida")
# ps_not_oyster <- ps %>% subset_taxa(Order != "Ostreoida")
# oyster_sums <- otu_table(ps_oyster)@.Data %>% apply(MARGIN = 2, sum)
# not_oyster_counts <- otu_table(ps_not_oyster)@.Data
#
# over_oyster <- sweep(not_oyster_counts, 2, oyster_sums, "/")
# over_oyster_log10 <- log10(over_oyster)
This is a variance normalizing tranformaiton. Its apparently an alternative to rarifying the data.
deseq_pre <- phyloseq_to_deseq2(ps, design = ~ Project)
converting counts to integer mode
# deseq_counts <- estimateSizeFactors(deseq_pre, type = "poscounts")
# deseq_counts_vst <- varianceStabilizingTransformation(deseq_counts)
# vst_trans_count_tab <- assay(deseq_counts_vst)
# Ella on Slack
#deseq_pre
dds = deseq_pre[rowSums(counts(deseq_pre)) > 5,]
dds_esf <- estimateSizeFactors(dds, type = "poscounts")
dds01 <- DESeq(dds_esf)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 851 genes
-- DESeq argument 'minReplicatesForReplace' = 7
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
dds_res <- results(dds01)
dds_counts <- counts(dds01, normalized = TRUE)
ps_dds <- ps_retax
otu_table(ps_dds) <- otu_table(dds_counts, taxa_are_rows = TRUE)
Select only species that show up in at least 20% of the samples.
ps_common <- filter_taxa(ps_dds, function(x) sum(x > 2) > (0.2*length(x)), TRUE)
ps_common
phyloseq-class experiment-level object
otu_table() OTU Table: [ 462 taxa and 35 samples ]
sample_data() Sample Data: [ 35 samples by 6 sample variables ]
tax_table() Taxonomy Table: [ 462 taxa by 9 taxonomic ranks ]
Devide the abundance of each microbial ASV by the host gene copy number. Now microbial genes are reported as ratios to host, rather than counts. This process adjusts for compositionality.
Normalize microibal counts to oyster counts
ps_oyster <- ps_dds %>% subset_taxa(Order == "Ostreoida")
ps_not_oyster <- ps_common %>% subset_taxa(Order != "Ostreoida") # ok if not common, so just using dds
oyster_sums <- otu_table(ps_oyster)@.Data %>% apply(MARGIN = 2, sum)
not_oyster_counts <- otu_table(ps_not_oyster)@.Data
over_oyster <- sweep(not_oyster_counts, 2, oyster_sums, "/")
# so this suddently contains zeros, making everything fail, but it didn't used to. What gives?
over_oyster_log10 <- log10(over_oyster)
(detection_thresh <- min(na.omit(over_oyster[over_oyster > 0])))
[1] 7.091444e-06
over_oyster_log10 <- log10(over_oyster + detection_thresh)
ps_oo <- ps_not_oyster
otu_table(ps_oo) <- otu_table(over_oyster, taxa_are_rows = TRUE)
ps_oo <- subset_samples(ps_oo, (SampleID %in% c(names(oyster_sums[oyster_sums > 0])))) # remove cases
ps_oo_log <- ps_not_oyster
otu_table(ps_oo_log) <- otu_table(over_oyster_log10, taxa_are_rows = TRUE)
ps_oo_log_ss <- subset_samples(ps_oo_log, !(Project =="Mock"& Strain == "Even" & Run == "NoCrash"))
ps_oo_ss <- subset_samples(ps_oo, !(Project =="Mock"& Strain == "Even" & Run == "NoCrash"))
Seeing which species relate to crash vs non-crash
We don’t show these figures in the paper, but we do refer to them.
Initial data wrangling
ps_oo_log_ss2 <- ps_oo_log_ss1
sample_data(ps_oo_log_ss2) <- mySamples %>% column_to_rownames("Sample") %>% sample_data()
Reshaping to long takes a little while. (~ 20 seconds)
melt_oo_log_ss2 <- psmelt(ps_oo_log_ss2)
melt2_oo_log_ss2 <- melt_oo_log_ss2 %>%
mutate(logAbundance = Abundance) %>%
mutate(Abundance = 10^(Abundance))
melt2_oo_log_ss2 <- melt2_oo_log_ss2 %>% left_join(mySpecies %>% select(RDA1.Spec = RDA1, JName), by = "JName")
melt2_oo_log_ss2 %>% head
Stuff
Run an lme to see if each microbe is related to project, holding out treatment as a mixed effect.
modframe <- melt2_oo_log_ss2 %>% select(Project:Group, logAbundance, Kingdom:Genus, JName ) %>% group_by(JName) %>% nest(data = Project:logAbundance) %>%
#mutate(mod = map(data, ~tidy(lm(data = ., logAbundance ~ Project)))) %>%
mutate(lme = map(data, ~tidy(lmer(data = ., logAbundance ~ Project + (1|Treatment) + (1|Strain)))))
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
Ignore the many warning messages, and don’t ask me what they mean.
And visualize the results
modframe01 <- modframe %>% unnest(lme) %>% filter(term == "ProjectNoCrash") %>% select(Kingdom:JName, estimate, std.error, p.value) %>% mutate(fdr = p.adjust(p.value, method = "BH"))
ggplotly(
ggplot(modframe01, aes(x = estimate, y = log10(p.value), color = Kingdom, JName = JName)) + geom_point() +
scale_color_manual(values = c(Bacteria = "Gray10", Eukaryota = "blue", Archaea = "red")) +
geom_hline(aes(yintercept = log10(0.01)))
)
Figure 2. Not shown int the paper. Estimate is the size of the coefficint in a linear model. log10 P value tells about significance.
Everything below the line is statistically significant. Mouse over the dots to see which bacteria are which. If plotly is giving you problems, comment out the ggplotly bits and this shows up as a normal plot. But then you can’t mouse over poitns.
How many significant and non significant ASVs are there?
modframe01 %>% ungroup %>% summarise(signif = sum(p.value < 0.01), total = length(p.value)) %>% mutate(frachits = signif/total)
63 % of the asvs are related to treatment p < 0.01.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBCcmluZyBpbiB0aGUgcGh5bG9zZXEgb2JqZWN0CgpgYGB7cn0Kc2V0LnNlZWQoMzMxMDApCiNzb3VyY2UoIlNjcmlwdHMvTWFrZVBoeWxvc2VxT2JqZWN0LlIiKQpsb2FkKCJJbnRlcm1lZGlhdGVEYXRhL2xhcnZhbF9waHlsb3NlcS5SRGF0YSIpCmBgYAoKIyBSZXF1aXJlZCBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHBoeWxvc2VxKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZlZ2FuKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KGJyb29tLm1peGVkKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeSh0aWR5dmVyc2UpCgpgYGAKCiMgQmV0dGVyIG5hbWVzIGZvciB0YXhhCgoKCmBgYHtyfQpwdWxsX2xvd2xldiA8LSBmdW5jdGlvbih0YXhhVmVjKXsKICB0YXhhVmVjIDwtIGRwbHlyOjpzZWxlY3QoS2luZ2RvbTpHZW51cykKICB0YXhhVmVjIDwtIG5hLm9taXQodGF4YVZlYykKICB0YXhhVmVjW2xlbmd0aCh0YXhhVmVjKV0KfQoKdGhlVGF4YSA8LSBwcyAlPiUgdGF4X3RhYmxlKCkgJT4lIGFzKCJtYXRyaXgiKSAlPiUgZGF0YS5mcmFtZSgpICAjJT4lIGFzLmRhdGEuZnJhbWUoKQp0aGVUYXhhW10gPC0gbGFwcGx5KHRoZVRheGEsIGFzLmNoYXJhY3RlcikKdGhlVGF4YSRBU1ZOdW0gPC0gcm93bmFtZXModGhlVGF4YSkgJT4lIHBhcnNlX251bWJlcgp0aGVUYXhhJEFTVk5hbWUgPC0gcm93bmFtZXModGhlVGF4YSkKCgpteUxvd2lzdCA8LSB0aGVUYXhhICU+JSBwaXZvdF9sb25nZXIoS2luZ2RvbTpHZW51cykgJT4lIG5hLm9taXQgJT4lIGdyb3VwX2J5KEFTVk5hbWUpICU+JSBzdW1tYXJpc2UoTG93aXN0ID0gbGFzdCh2YWx1ZSkpCgp0aGVUYXhhIDwtIHRoZVRheGEgJT4lIGxlZnRfam9pbihteUxvd2lzdCwgYnkgPSAiQVNWTmFtZSIpCnRoZVRheGEgPC0gdGhlVGF4YSAlPiUgbXV0YXRlKEpOYW1lID0gc3RyX2MoTG93aXN0LCBBU1ZOdW0sIHNlcCA9ICJfIikpCnRoZVRheGEgPC0gdGhlVGF4YSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJBU1ZOYW1lIikKaGVhZCh0aGVUYXhhKQoKCnR0MiA8LSB0YXhfdGFibGUodGhlVGF4YSkKcm93bmFtZXModHQyKSA8LSByb3duYW1lcyh0aGVUYXhhKQpjb2xuYW1lcyh0dDIpIDwtIGNvbG5hbWVzKHRoZVRheGEpCnBzX3JldGF4IDwtIHBzCnRheF90YWJsZShwc19yZXRheCkgPC0gdHQyCmBgYAoKUmVtb3ZlIGJsYW5rcwpgYGB7cn0KcHNfbm9ibGFuayA8LSBzdWJzZXRfc2FtcGxlcyhwc19yZXRheCwgU3RyYWluICE9ICJCbGFuayIpCnBzX3BsdXNvbmUgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMocHNfbm9ibGFuaywgZnVuY3Rpb24oeCkgeCArIDEpCmBgYAoKQ29udmVydCB0byByZWxhdGl2ZSBhYnVuZGFuY2UKYGBge3J9CnBzcmEgPC0gcHMgJT4lIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKCBmdW5jdGlvbih4KSB4L3N1bSh4KSkKYGBgCgpOb3JtYWxpemUgbWljcm9pYmFsIGNvdW50cyB0byBveXN0ZXIgY291bnRzCmBgYHtyfQojIHBzX295c3RlciA8LSBwcyAlPiUgc3Vic2V0X3RheGEoT3JkZXIgPT0gIk9zdHJlb2lkYSIpCiMgcHNfbm90X295c3RlciA8LSBwcyAlPiUgc3Vic2V0X3RheGEoT3JkZXIgIT0gIk9zdHJlb2lkYSIpCiMgb3lzdGVyX3N1bXMgPC0gb3R1X3RhYmxlKHBzX295c3RlcilALkRhdGEgJT4lIGFwcGx5KE1BUkdJTiA9IDIsIHN1bSkKIyBub3Rfb3lzdGVyX2NvdW50cyA8LSBvdHVfdGFibGUocHNfbm90X295c3RlcilALkRhdGEKIyAKIyBvdmVyX295c3RlciA8LSBzd2VlcChub3Rfb3lzdGVyX2NvdW50cywgMiwgb3lzdGVyX3N1bXMsICIvIikKIyBvdmVyX295c3Rlcl9sb2cxMCA8LSBsb2cxMChvdmVyX295c3RlcikKYGBgCgpUaGlzIGlzIGEgdmFyaWFuY2Ugbm9ybWFsaXppbmcgdHJhbmZvcm1haXRvbi4gSXRzIGFwcGFyZW50bHkgYW4gYWx0ZXJuYXRpdmUgdG8gcmFyaWZ5aW5nIHRoZSBkYXRhLiAKYGBge3J9CmRlc2VxX3ByZSA8LSBwaHlsb3NlcV90b19kZXNlcTIocHMsIGRlc2lnbiA9IH4gUHJvamVjdCkKIyBkZXNlcV9jb3VudHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZXNlcV9wcmUsIHR5cGUgPSAicG9zY291bnRzIikKIyBkZXNlcV9jb3VudHNfdnN0IDwtIHZhcmlhbmNlU3RhYmlsaXppbmdUcmFuc2Zvcm1hdGlvbihkZXNlcV9jb3VudHMpCiMgdnN0X3RyYW5zX2NvdW50X3RhYiA8LSBhc3NheShkZXNlcV9jb3VudHNfdnN0KQoKIyBFbGxhIG9uIFNsYWNrCiNkZXNlcV9wcmUKZGRzID0gZGVzZXFfcHJlW3Jvd1N1bXMoY291bnRzKGRlc2VxX3ByZSkpID4gNSxdCmRkc19lc2YgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHMsIHR5cGUgPSAicG9zY291bnRzIikKZGRzMDEgPC0gREVTZXEoZGRzX2VzZikKZGRzX3JlcyA8LSByZXN1bHRzKGRkczAxKQoKZGRzX2NvdW50cyA8LSBjb3VudHMoZGRzMDEsIG5vcm1hbGl6ZWQgPSBUUlVFKQoKcHNfZGRzIDwtIHBzX3JldGF4Cm90dV90YWJsZShwc19kZHMpIDwtIG90dV90YWJsZShkZHNfY291bnRzLCB0YXhhX2FyZV9yb3dzID0gVFJVRSkKYGBgCgpTZWxlY3Qgb25seSBzcGVjaWVzIHRoYXQgc2hvdyB1cCBpbiBhdCBsZWFzdCAyMCUgb2YgdGhlIHNhbXBsZXMuIAoKYGBge3J9CnBzX2NvbW1vbiA8LSBmaWx0ZXJfdGF4YShwc19kZHMsIGZ1bmN0aW9uKHgpIHN1bSh4ID4gMikgPiAoMC4yKmxlbmd0aCh4KSksIFRSVUUpCnBzX2NvbW1vbgpgYGAKCkRldmlkZSB0aGUgYWJ1bmRhbmNlIG9mIGVhY2ggbWljcm9iaWFsIEFTViBieSB0aGUgaG9zdCBnZW5lIGNvcHkgbnVtYmVyLiBOb3cgbWljcm9iaWFsIGdlbmVzIGFyZSByZXBvcnRlZCBhcyByYXRpb3MgdG8gaG9zdCwgcmF0aGVyIHRoYW4gY291bnRzLgpUaGlzIHByb2Nlc3MgYWRqdXN0cyBmb3IgY29tcG9zaXRpb25hbGl0eS4KCk5vcm1hbGl6ZSBtaWNyb2liYWwgY291bnRzIHRvIG95c3RlciBjb3VudHMKCmBgYHtyfQpwc19veXN0ZXIgPC0gcHNfZGRzICU+JSBzdWJzZXRfdGF4YShPcmRlciA9PSAiT3N0cmVvaWRhIikKcHNfbm90X295c3RlciA8LSBwc19jb21tb24gJT4lIHN1YnNldF90YXhhKE9yZGVyICE9ICJPc3RyZW9pZGEiKSAjIG9rIGlmIG5vdCBjb21tb24sIHNvIGp1c3QgdXNpbmcgZGRzCm95c3Rlcl9zdW1zIDwtIG90dV90YWJsZShwc19veXN0ZXIpQC5EYXRhICU+JSBhcHBseShNQVJHSU4gPSAyLCBzdW0pCm5vdF9veXN0ZXJfY291bnRzIDwtIG90dV90YWJsZShwc19ub3Rfb3lzdGVyKUAuRGF0YQoKb3Zlcl9veXN0ZXIgPC0gc3dlZXAobm90X295c3Rlcl9jb3VudHMsIDIsIG95c3Rlcl9zdW1zLCAiLyIpCiMgc28gdGhpcyBzdWRkZW50bHkgY29udGFpbnMgemVyb3MsIG1ha2luZyBldmVyeXRoaW5nIGZhaWwsIGJ1dCBpdCBkaWRuJ3QgdXNlZCB0by4gV2hhdCBnaXZlcz8Kb3Zlcl9veXN0ZXJfbG9nMTAgPC0gbG9nMTAob3Zlcl9veXN0ZXIpCihkZXRlY3Rpb25fdGhyZXNoIDwtIG1pbihuYS5vbWl0KG92ZXJfb3lzdGVyW292ZXJfb3lzdGVyID4gMF0pKSkKb3Zlcl9veXN0ZXJfbG9nMTAgPC0gbG9nMTAob3Zlcl9veXN0ZXIgKyBkZXRlY3Rpb25fdGhyZXNoKQoKcHNfb28gPC0gcHNfbm90X295c3RlcgpvdHVfdGFibGUocHNfb28pIDwtIG90dV90YWJsZShvdmVyX295c3RlciwgdGF4YV9hcmVfcm93cyA9IFRSVUUpCnBzX29vIDwtIHN1YnNldF9zYW1wbGVzKHBzX29vLCAoU2FtcGxlSUQgJWluJSBjKG5hbWVzKG95c3Rlcl9zdW1zW295c3Rlcl9zdW1zID4gMF0pKSkpICMgcmVtb3ZlIGNhc2VzCgpwc19vb19sb2cgPC0gcHNfbm90X295c3RlcgpvdHVfdGFibGUocHNfb29fbG9nKSA8LSBvdHVfdGFibGUob3Zlcl9veXN0ZXJfbG9nMTAsIHRheGFfYXJlX3Jvd3MgPSBUUlVFKQoKcHNfb29fbG9nX3NzIDwtIHN1YnNldF9zYW1wbGVzKHBzX29vX2xvZywgIShQcm9qZWN0ID09Ik1vY2siJiBTdHJhaW4gPT0gIkV2ZW4iICYgUnVuID09ICJOb0NyYXNoIikpCnBzX29vX3NzIDwtIHN1YnNldF9zYW1wbGVzKHBzX29vLCAhKFByb2plY3QgPT0iTW9jayImIFN0cmFpbiA9PSAiRXZlbiIgJiBSdW4gPT0gIk5vQ3Jhc2giKSkKYGBgCgojIE1ha2UgdGhlIG9uZSBmaWd1cmUgZm9yIHRoZSBwYXBlcgoKIyMgQSBmdW5jdGlvbiBJIHVzZQpjb252ZXJ0cyBwaHlsb3NlcSBzYW1wbGUgZGF0YSB0byBhIGRhdGEgZnJhbWUKYGBge3J9CnNhbWRfdG9fZGYgPC0gZnVuY3Rpb24oc2FtZCl7CiAgZGYgPC0gc2FtZCAlPiUgc2FtcGxlX2RhdGEgJT4lIC5ALkRhdGEgJT4lIGxhcHBseShhcy5jaGFyYWN0ZXIpICU+JSBkYXRhLmZyYW1lCiAgY29sbmFtZXMoZGYpIDwtIHNhbWRAbmFtZXMKICByb3duYW1lcyhkZikgPC0gc2FtZEByb3cubmFtZXMKICBkZgp9CmBgYAoKIyMgQWN0dWFsbHkgcnVuIHRoZSBSREEKUmVkdW5kYW5jeSBhbmFseXNpcyAoUkRBKSBpcyBhbiBvcmRpbmF0aW9uIGFwcHJvYWNoIHRoYXQgYWxsb3dzIHVzIHRvIGxvb2sgYXQgaG93IHRoZSBtaWNyb2JpYWwgY29tbXVuaXR5IGFzIGEgd2hvbGUgdmFyaWVzIGFjcm9zcyBvdXIgdmFyaWFibGVzIG9mIGludGVyZXN0LgpgYGB7cn0KCnBzX29vX2xvZ19zczEgPC0gcHNfb29fbG9nX3NzICU+JSBzdWJzZXRfc2FtcGxlcygoKFJ1biA9PSJOb0NyYXNoIiAmIFByb2plY3QgPT0gIk5vQ3Jhc2giKSB8IChSdW4gPT0gIkNyYXNoNCIgJiBQcm9qZWN0ID09ICJDcmFzaCIpKSAmIFRyZWF0bWVudCAlaW4lIGMoIkZlZCIsICJTdGFydmUiLCAiUHJlIikpCgp0ZXN0X3JkYSA8LSByZGEodChvdHVfdGFibGUocHNfb29fbG9nX3NzMSkpIH4gUHJvamVjdCArIGFzLmZhY3RvcihTdHJhaW4gPT0gIldpbGQiKSArIGFzLmZhY3RvcihUcmVhdG1lbnQgPT0gIkZlZCIpLCBkYXRhID0gc2FtZF90b19kZihzYW1wbGVfZGF0YShwc19vb19sb2dfc3MxKSkpCnRlc3RfcmRhCnRlc3RfcmRhX2Fub3ZhIDwtIGFub3ZhKHRlc3RfcmRhLCBieSA9ICJtYXJnaW4iLCBwZXJtdXRhdGlvbnMgPSBob3cobnBlcm0gPSA5OTk5OSkpCnRlc3RfcmRhX2Fub3ZhCm15U2NvcmVzIDwtIHNjb3Jlcyh0ZXN0X3JkYSwgY2hvaWNlcyA9IGMoMTo0KSwgc2NhbGluZyA9ICJzeW1tZXRyaWMiKQpgYGAKCiMjIFBvc3QgcHJvY2VzaW5nIG9mIFJEQQoKIyMjIFNhbXBsZSBEYXRhCmBgYHtyfQpteVNhbXBsZXMgPC0gbGVmdF9qb2luKApteVNjb3JlcyRzaXRlcyAlPiUgZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJTYW1wbGUiKSwKcHNfb29fbG9nX3NzMSAlPiUgc2FtcGxlX2RhdGEoKSAlPiUgc2FtZF90b19kZiAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJTYW1wbGUiKSwKYnkgPSAiU2FtcGxlIgopCmBgYAoKIyMjIFBlcmNlbnQgdmFyaWFuY2UgZXhwbGFpbmVkCmBgYHtyfQplaWdzdW0gPC0gc3VtKGModGVzdF9yZGEkQ0NBJGVpZywgdGVzdF9yZGEkQ0EkZWlnKSkKY2NhX2VpZyA8LSB0ZXN0X3JkYSRDQ0EkZWlnIC8gZWlnc3VtCmNhX2VpZyA8LSB0ZXN0X3JkYSRDQSRlaWcgLyBlaWdzdW0KYWxsX2VpZyA8LSBjKGNjYV9laWcsIGNhX2VpZykKYWxsX2VpZ1siUkRBMSJdIApgYGAKCmBgYHtyfQpyZGFfZWlnX3BjdCA8LSBkYXRhLmZyYW1lKGFsbF9laWcpICU+JSByb3duYW1lc190b19jb2x1bW4oIkF4IikgJT4lIGRwbHlyOjpyZW5hbWUoRWlnUGN0ID0gImFsbF9laWciKSAlPiUKICBtdXRhdGUoQXhpczIgPSBvcmRlcmVkKEF4LCBsZXZlbHMgPSBBeCkpCnJkYV9laWdfcGN0CmBgYAoKIyMjIFNwZWNpZXMKU2VsZWN0IHdoaWNoIHNwZWNpZXMgd2Ugd2lsbCBzaG93IGluIHRoZSBmaWd1cmUsIHRhcmdldGluZyBvbmVzIHRoYXQgYXJlIGZhciBmcm9tIDAsIDAuCmBgYHtyfQpteVNwZWNpZXMgPC0gbGVmdF9qb2luKAogIG15U2NvcmVzJHNwZWNpZXMgJT4lIGRhdGEuZnJhbWUgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiQVNWIiksCnBzX29vX2xvZ19zczEgJT4lIHRheF90YWJsZSgpICU+JSAuQC5EYXRhICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIkFTViIpCikgJT4lIG11dGF0ZShSREFEaXN0ID0gc3FydChSREExXjIgKyBSREEyXjIgKyBSREEzIF4yKSkgJT4lIAogIGFycmFuZ2UoLVJEQURpc3QpICU+JQogIGhlYWQoMTApICU+JQogIG11dGF0ZShSYW5rID0gMToxMCkKYGBgCgojIyBDYWxjdWxhdGUgQ2VudHJvaWRzCkNlbnRyb2lkcyB0ZWxsIHVzIGFib3V0IHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IHR5cGVzIGFuZCBob3cgdGhleSByZWxhdGUgdG8gdGhlIHNhbXBsZXMgYW5kIHNwZWNpZXMuCmBgYHtyfQpteUNlbnQgPC0gbXlTY29yZXMkY2VudHJvaWRzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIlRyZWF0bWVudCIpICU+JQogIHRpZHlyOjpleHRyYWN0KFRyZWF0bWVudCwgYygiVHlwZSIsICJDb25kaXRpb24iKSwgIihbQS1aXVthLXpdKykoW0EtWl0uKikiLCAgcmVtb3ZlID0gRkFMU0UpICU+JQogIG11dGF0ZShUeXBlID0gaWZfZWxzZShzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIldpbGQiKSwgIlN0cmFpbiIsIFR5cGUpKSAlPiUKICBtdXRhdGUoQ29uZGl0aW9uID0gaWZfZWxzZShUeXBlID09ICJTdHJhaW4iICYgc3RyX2RldGVjdChUcmVhdG1lbnQsICJGQUxTRSIpLCAiVGFtZSIsIENvbmRpdGlvbikpICU+JQogIG11dGF0ZShDb25kaXRpb24gPSBpZl9lbHNlKFR5cGUgPT0gIlN0cmFpbiIgJiBzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIlRSVUUiKSwgIldpbGQiLCBDb25kaXRpb24pKSAlPiUKICBtdXRhdGUoVHlwZSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChUcmVhdG1lbnQsICJGZWQiKSwgIkZlZWRpbmciLCBUeXBlKSkgJT4lCiAgbXV0YXRlKENvbmRpdGlvbiA9IGlmX2Vsc2UoVHlwZSA9PSAiRmVlZGluZyIgJiBzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIkZBTFNFIiksICJTdGFydmVkT3JQcmUiLCBDb25kaXRpb24pKSAlPiUKICBtdXRhdGUoQ29uZGl0aW9uID0gaWZfZWxzZShUeXBlID09ICJGZWVkaW5nIiAmIHN0cl9kZXRlY3QoVHJlYXRtZW50LCAiVFJVRSIpLCAiRmVkIiwgQ29uZGl0aW9uKSkKCm15Q2VudDIgPC0gbXlDZW50ICU+JSBmaWx0ZXIoKENvbmRpdGlvbiAlaW4lIGMoIkZlZCIsICJXaWxkIiwgIkNyYXNoIikpKQpgYGAKCiMjIyBNYWluIEZpZ3VyZQpUaGlzIGFjdHVhbGx5IHBsb3RzIHRoZSBtYWluIGZpZ3VyZSwgdXNpbmcgZ2dwbG90CmBgYHtyfQpjY2FQbG90XzFWMl9BMCA8LSBteVNhbXBsZXMgJT4lIGdncGxvdChhZXMoeCA9IFJEQTEsIHkgPSBSREEyKSkgKyAjIG5lZ2F0aXZlIGJlY2F1c2UgYXQgc29tZSBwb2ludCBpbiB0aGUgYW5hbHlzaXMgdGhlIFJEQSBzcG9udGFuZW91c2x5IGZsaXBwZWQgYW5kIHNpZ24gZG9zbid0IGFjdHVhbGx5IG1hdHRlcgogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHN0cm9rZSA9IDMsIGFlcyhzaGFwZSA9IFByb2plY3QsIGNvbG9yID0gU3RyYWluID09ICJXaWxkIiwgZmlsbCA9IFRyZWF0bWVudCksIGFscGhhID0gMSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIyLDIxKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JheTQwIiwgImJsYWNrIikpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhGZWQgPSAiQmx1ZSIsIFByZSA9ICJEYXJrR3JlZW4iLCBTdGFydmUgPSAiT3JhbmdlIikpKwogIGdlb21fcG9pbnQoZGF0YSA9IG15U3BlY2llcywgc2l6ZSA9IDQsIHNoYXBlID0gIisiKSArIAogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhID0gbXlTcGVjaWVzLCBhZXMobGFiZWwgPSBKTmFtZSkgLCBzaXplID0gMykgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSAyMSkpLCBjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjEpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKY2NhUGxvdF8xVjJfQSA8LSBjY2FQbG90XzFWMl9BMCAgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKEZlZCA9ICJCbHVlIiwgUHJlID0gIkRhcmtHcmVlbiIsIFN0YXJ2ZSA9ICJPcmFuZ2UiLCBDcmFzaCA9ICJQaW5rIiwgTm9DcmFzaCA9ICJXaGl0ZSIsIFdpbGQgPSAiV2hpdGUiKSkKCmNjYUxlZ2VuZCA8LSBnZXRfbGVnZW5kKGNjYVBsb3RfMVYyX0EwKQoKY2NhUGxvdF8xVjJfQiA8LSBjY2FQbG90XzFWMl9BICsKICBnZW9tX2xhYmVsKGRhdGEgPSBteUNlbnQyLCBhZXMobGFiZWwgPSBDb25kaXRpb24sIHggPSBSREExICogMS43NSwgeSA9IFJEQTIgKiAyLCBmaWxsID0gQ29uZGl0aW9uKSAsIHNpemUgPSA1KSArCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBteUNlbnQyLCBhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gUkRBMSAqIDIuNSwgeWVuZCA9IFJEQTIgKiAyLjUpLCBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAiaW4iKSksIGFscGhhID0gMC41LCBzaXplID0gMSkgKwogIGNvb3JkX2ZpeGVkKHNxcnQodGVzdF9yZGEkQ0NBJGVpZ1syXS90ZXN0X3JkYSRDQ0EkZWlnWzFdKSkgKwogIGxhYnMoeCA9IHBhc3RlMCgiUkRBMSIsICIgKCIsIHNjYWxlczo6cGVyY2VudChhbGxfZWlnWyJSREExIl0pLCAiKSIpLAogICAgICAgeSA9IHBhc3RlMCgiUkRBMiIsICIgKCIsIHNjYWxlczo6cGVyY2VudChhbGxfZWlnWyJSREEyIl0pLCAiKSIpCiAgICAgICApICsKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojcGxvdF9ncmlkKGNjYVBsb3RfMVYyX0IsIGNjYUxlZ2VuZCkKUHJvdG9OZXdGaWcgPC0gcGxvdF9ncmlkKGNjYVBsb3RfMVYyX0IsIGNjYUxlZ2VuZCwgbnJvdyA9IDIsIHJlbF9oZWlnaHRzID0gYygxMCwxKSkKZ2dzYXZlKCJGaWd1cmVzL1Byb3RvTmV3RmlnLnN2ZyIsIFByb3RvTmV3RmlnLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKIyMjIFZpZXcgTWFpbiBGaWd1cmUKClRoZSBtYWluIGZpZ3VyZSBpcyBhbiAuc3ZnIGZpbGUsIGJ1dCBoZXJlIGlzIGEgZHJhZnQgdmlldyBmb3IgdGhpcyB3b3JrYm9vay4KYGBge3J9ClByb3RvTmV3RmlnCmBgYAoKSSdtIG5vdCBzdXJlIHdoeSAiTm9DcmFzaCIgYW5kICJXaWxkIiBzaG93ZWQgdXAgaW4gdGhlIGxlZ2VuZC4gSXQgZGlkbid0IHVzZWQgdG8gZG8gdGhhdCwgYnV0IEknbSBub3QgZ29pbmcgdG8gYm90aGVyIHRvIGNvcnJlY3QgdGhpcyByaWdodCBub3cuCkdHcGxvdCB1cGdyYWRlCgojIyBCbGFjayBhbmQgd2hpdGUgdmVyc2lvbgoKIyMjIE1haW4gRmlndXJlCgpUaGlzIGFjdHVhbGx5IHBsb3RzIHRoZSBtYWluIGZpZ3VyZSwgdXNpbmcgZ2dwbG90CgpgYGB7cn0KY2NhUGxvdF8xVjJfQTAgPC0gbXlTYW1wbGVzICU+JSBnZ3Bsb3QoYWVzKHggPSBSREExLCB5ID0gUkRBMikpICsgIyBuZWdhdGl2ZSBiZWNhdXNlIGF0IHNvbWUgcG9pbnQgaW4gdGhlIGFuYWx5c2lzIHRoZSBSREEgc3BvbnRhbmVvdXNseSBmbGlwcGVkIGFuZCBzaWduIGRvc24ndCBhY3R1YWxseSBtYXR0ZXIKICBnZW9tX3BvaW50KHNpemUgPSAzLCBzdHJva2UgPSAzLCBhZXMoc2hhcGUgPSBQcm9qZWN0LCBjb2xvciA9IFN0cmFpbiA9PSAiV2lsZCIsIGZpbGwgPSBUcmVhdG1lbnQpLCBhbHBoYSA9IDEpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYyhDcmFzaCA9IDIxLCBOb0NyYXNoID0gMjIpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJncmF5NDAiLCAiYmxhY2siKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKEZlZCA9ICJXaGl0ZSIsIFByZSA9ICJHcmV5IiwgU3RhcnZlID0gIkJsYWNrIikpKwogIGdlb21fcG9pbnQoZGF0YSA9IG15U3BlY2llcywgc2l6ZSA9IDQsIHNoYXBlID0gIisiKSArIAogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhID0gbXlTcGVjaWVzLCBhZXMobGFiZWwgPSBKTmFtZSkgLCBzaXplID0gMykgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSAyMSkpLCBjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjEpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKY2NhUGxvdF8xVjJfQSA8LSBjY2FQbG90XzFWMl9BMCAgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKEZlZCA9ICJXaGl0ZSIsIFByZSA9ICJHcmV5IiwgU3RhcnZlID0gIkJsYWNrIiwgQ3Jhc2ggPSAiV2hpdGUiLCBOb0NyYXNoID0gIldoaXRlIiwgV2lsZCA9ICJXaGl0ZSIpKQoKY2NhTGVnZW5kIDwtIGdldF9sZWdlbmQoY2NhUGxvdF8xVjJfQTApCgpjY2FQbG90XzFWMl9CIDwtIGNjYVBsb3RfMVYyX0EgKwogIGdlb21fdGV4dChkYXRhID0gbXlDZW50MiwgYWVzKGxhYmVsID0gQ29uZGl0aW9uLCB4ID0gUkRBMSAqIDEuNzUsIHkgPSBSREEyICogMiwgZmlsbCA9IENvbmRpdGlvbikgLCBzaXplID0gNSkgKwogIGdlb21fc2VnbWVudChkYXRhID0gbXlDZW50MiwgYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IFJEQTEgKiAxLjUsIHllbmQgPSBSREEyICogMS43NSksIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJpbiIpKSwgYWxwaGEgPSAwLjUsIHNpemUgPSAxKSArCiAgY29vcmRfZml4ZWQoc3FydCh0ZXN0X3JkYSRDQ0EkZWlnWzJdL3Rlc3RfcmRhJENDQSRlaWdbMV0pKSArCiAgbGFicyh4ID0gcGFzdGUwKCJSREExIiwgIiAoIiwgc2NhbGVzOjpwZXJjZW50KGFsbF9laWdbIlJEQTEiXSksICIpIiksCiAgICAgICB5ID0gcGFzdGUwKCJSREEyIiwgIiAoIiwgc2NhbGVzOjpwZXJjZW50KGFsbF9laWdbIlJEQTIiXSksICIpIikKICAgICAgICkgKwogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiNwbG90X2dyaWQoY2NhUGxvdF8xVjJfQiwgY2NhTGVnZW5kKQpQcm90b05ld0ZpZ0JXIDwtIHBsb3RfZ3JpZChjY2FQbG90XzFWMl9CLCBjY2FMZWdlbmQsIG5yb3cgPSAyLCByZWxfaGVpZ2h0cyA9IGMoMTAsMSkpCmdnc2F2ZSgiRmlndXJlcy9Qcm90b05ld0ZpZ0JXLnN2ZyIsIFByb3RvTmV3RmlnQlcsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgpgYGB7cn0KUHJvdG9OZXdGaWdCVwpgYGAKCgojIFNlZWluZyB3aGljaCBzcGVjaWVzIHJlbGF0ZSB0byBjcmFzaCB2cyBub24tY3Jhc2gKV2UgZG9uJ3Qgc2hvdyB0aGVzZSBmaWd1cmVzIGluIHRoZSBwYXBlciwgYnV0IHdlIGRvIHJlZmVyIHRvIHRoZW0uCgojIyBJbml0aWFsIGRhdGEgd3JhbmdsaW5nCgpgYGB7cn0KcHNfb29fbG9nX3NzMiA8LSBwc19vb19sb2dfc3MxCnNhbXBsZV9kYXRhKHBzX29vX2xvZ19zczIpIDwtICBteVNhbXBsZXMgJT4lIGNvbHVtbl90b19yb3duYW1lcygiU2FtcGxlIikgJT4lIHNhbXBsZV9kYXRhKCkKYGBgCgpSZXNoYXBpbmcgdG8gbG9uZyB0YWtlcyBhIGxpdHRsZSB3aGlsZS4gKH4gMjAgc2Vjb25kcykKYGBge3J9Cm1lbHRfb29fbG9nX3NzMiA8LSBwc21lbHQocHNfb29fbG9nX3NzMikKYGBgCgpgYGB7cn0KbWVsdDJfb29fbG9nX3NzMiA8LSBtZWx0X29vX2xvZ19zczIgJT4lCiAgbXV0YXRlKGxvZ0FidW5kYW5jZSA9IEFidW5kYW5jZSkgJT4lCiAgbXV0YXRlKEFidW5kYW5jZSA9IDEwXihBYnVuZGFuY2UpKQptZWx0Ml9vb19sb2dfc3MyIDwtIG1lbHQyX29vX2xvZ19zczIgJT4lIGxlZnRfam9pbihteVNwZWNpZXMgJT4lIHNlbGVjdChSREExLlNwZWMgPSBSREExLCBKTmFtZSksIGJ5ID0gIkpOYW1lIikKbWVsdDJfb29fbG9nX3NzMiAlPiUgaGVhZApgYGAKCiMjIFN0dWZmCgpSdW4gYW4gbG1lIHRvIHNlZSBpZiBlYWNoIG1pY3JvYmUgaXMgcmVsYXRlZCB0byBwcm9qZWN0LCBob2xkaW5nIG91dCB0cmVhdG1lbnQgYXMgYSBtaXhlZCBlZmZlY3QuCgpgYGB7cn0KbW9kZnJhbWUgPC0gbWVsdDJfb29fbG9nX3NzMiAlPiUgc2VsZWN0KFByb2plY3Q6R3JvdXAsIGxvZ0FidW5kYW5jZSwgS2luZ2RvbTpHZW51cywgSk5hbWUgKSAlPiUgZ3JvdXBfYnkoSk5hbWUpICU+JSBuZXN0KGRhdGEgPSBQcm9qZWN0OmxvZ0FidW5kYW5jZSkgJT4lCiAgI211dGF0ZShtb2QgPSBtYXAoZGF0YSwgfnRpZHkobG0oZGF0YSA9IC4sIGxvZ0FidW5kYW5jZSB+IFByb2plY3QpKSkpICU+JQogIG11dGF0ZShsbWUgPSBtYXAoZGF0YSwgfnRpZHkobG1lcihkYXRhID0gLiwgbG9nQWJ1bmRhbmNlIH4gUHJvamVjdCArICgxfFRyZWF0bWVudCkgKyAoMXxTdHJhaW4pKSkpKQpgYGAKSWdub3JlIHRoZSBtYW55IHdhcm5pbmcgbWVzc2FnZXMsIGFuZCBkb24ndCBhc2sgbWUgd2hhdCB0aGV5IG1lYW4uCgpBbmQgdmlzdWFsaXplIHRoZSByZXN1bHRzCmBgYHtyfQptb2RmcmFtZTAxIDwtIG1vZGZyYW1lICU+JSB1bm5lc3QobG1lKSAlPiUgZmlsdGVyKHRlcm0gPT0gIlByb2plY3ROb0NyYXNoIikgJT4lIHNlbGVjdChLaW5nZG9tOkpOYW1lLCBlc3RpbWF0ZSwgc3RkLmVycm9yLCBwLnZhbHVlKSAlPiUgbXV0YXRlKGZkciA9IHAuYWRqdXN0KHAudmFsdWUsIG1ldGhvZCA9ICJCSCIpKQpgYGAKCmBgYHtyfQpnZ3Bsb3RseSgKZ2dwbG90KG1vZGZyYW1lMDEsIGFlcyh4ID0gZXN0aW1hdGUsIHkgPSBsb2cxMChwLnZhbHVlKSwgIGNvbG9yID0gS2luZ2RvbSwgSk5hbWUgPSBKTmFtZSkpICsgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoQmFjdGVyaWEgPSAiR3JheTEwIiwgRXVrYXJ5b3RhID0gImJsdWUiLCBBcmNoYWVhID0gInJlZCIpKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IGxvZzEwKDAuMDEpKSkKKQpgYGAKCkZpZ3VyZSAyLiBOb3Qgc2hvd24gaW50IHRoZSBwYXBlci4gRXN0aW1hdGUgaXMgdGhlIHNpemUgb2YgdGhlIGNvZWZmaWNpbnQgaW4gYSBsaW5lYXIgbW9kZWwuIGxvZzEwIFAgdmFsdWUgdGVsbHMgYWJvdXQgc2lnbmlmaWNhbmNlLiAKCkV2ZXJ5dGhpbmcgYmVsb3cgdGhlIGxpbmUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gTW91c2Ugb3ZlciB0aGUgZG90cyB0byBzZWUgd2hpY2ggYmFjdGVyaWEgYXJlIHdoaWNoLiBJZiBwbG90bHkgaXMgZ2l2aW5nIHlvdSBwcm9ibGVtcywgY29tbWVudCBvdXQgdGhlIGBnZ3Bsb3RseWAgYml0cyBhbmQgdGhpcyBzaG93cyB1cCBhcyBhIG5vcm1hbCBwbG90LiBCdXQgdGhlbiB5b3UgY2FuJ3QgbW91c2Ugb3ZlciBwb2l0bnMuCgpIb3cgbWFueSBzaWduaWZpY2FudCBhbmQgbm9uIHNpZ25pZmljYW50IEFTVnMgYXJlIHRoZXJlPwoKYGBge3J9Cm1vZGZyYW1lMDEgJT4lIHVuZ3JvdXAgJT4lIHN1bW1hcmlzZShzaWduaWYgPSBzdW0ocC52YWx1ZSA8IDAuMDEpLCB0b3RhbCA9IGxlbmd0aChwLnZhbHVlKSkgJT4lIG11dGF0ZShmcmFjaGl0cyA9IHNpZ25pZi90b3RhbCkgCmBgYAo2MyAlIG9mIHRoZSBhc3ZzIGFyZSByZWxhdGVkIHRvIHRyZWF0bWVudCBwIDwgMC4wMS4KCiMgU2Vzc2lvbiBJbmZvIER1bXAKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoK